#pragma once
#include "KeeperBasic.h"
#include "KeeperLoggerWrapper.h"
#include "KeeperMessage.h"
#include "KeeperStateMachine.h"
#include "KeeperStateMgr.h"
#include "NuRaft.h"

static const nuraft::raft_params::return_method_type CALL_TYPE =
    nuraft::raft_params::blocking;
//  = raft_params::async_handler;


namespace keeper_server {

using raft_result = nuraft::cmd_result<NuRaftBufferPtr>;

class KeeperServer {
 public:
  KeeperServer()
      : server_id_(1),
        addr_("localhost"),
        port_(25000),
        raft_logger_(nullptr),
        sm_(nullptr),
        smgr_(nullptr),
        raft_instance_(nullptr),
        initialized_flag(false) {}

  void reset() {
    raft_logger_.reset();
    sm_.reset();
    smgr_.reset();
    raft_instance_.reset();
  }
  bool is_leader() { return raft_instance_->is_leader(); }
  bool is_leader_alive() { return raft_instance_->is_leader_alive(); }

  void append_log(const std::string& cmd,
                  const std::vector<std::string>& tokens);
  void append_kv(const std::string& cmd,
                 const std::vector<std::string>& tokens);
  void get_kv(const std::string& cmd, const std::vector<std::string>& tokens);

  void print_status(const std::string& cmd,
                    const std::vector<std::string>& tokens);

  void help(const std::string& cmd, const std::vector<std::string>& tokens);

  bool do_cmd(const std::vector<std::string>& tokens);
  void add_server(const std::string& cmd,
                  const std::vector<std::string>& tokens);
  void server_list(const std::string& cmd,
                   const std::vector<std::string>& tokens);
  std::vector<std::string> tokenize(const char* str, char c = ' ');
  void loop();
  void init_raft();
  void set_server_info(int argc, char** argv);

  int get_server_id() { return server_id_; }
  std::string get_endpoint() { return endpoint_; }
  nuraft::cb_func::ReturnCode callbackFunc(nuraft::cb_func::Type type,
                                           nuraft::cb_func::Param* param);
  void wait_init();

  void send_to_nuraft(nuraft::ptr<nuraft::buffer>& logPtr);
  std::string read_leveldb(const std::string& k) {
    return sm_->read_leveldb(k);
  }

 private:
  // Server ID.
  int server_id_;

  // Server address.
  std::string addr_;

  // Server port.
  int port_;

  // Endpoint: `<addr>:<port>`.
  std::string endpoint_;

  // Logger.
  nuraft::ptr<nuraft::logger> raft_logger_;

  // State machine.
  nuraft::ptr<KeeperStateMachine> sm_;

  // State manager.
  nuraft::ptr<KeeperStateMgr> smgr_;

  // Raft launcher.
  nuraft::raft_launcher launcher_;

  // Raft server instance.
  nuraft::ptr<nuraft::raft_server> raft_instance_;
  nuraft::ptr<nuraft::asio_service> asio_service;
  nuraft::ptr<nuraft::rpc_listener> asio_listener;
  std::atomic<bool> initialized_flag;
  std::condition_variable initialized_cv;
  std::mutex initialized_mutex;
  std::string leveldb_path;
};

};  // namespace keeper_server
